﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace Kokomo.Mirror.Ia32
{
	public class IaOpCodeLookup
	{
		static IaOpCodeLookup()
		{
			//Lock = new OpCode(0xF0, 1, OpCodeForm.Normal, Mnemonic.@lock, OpCodeType.PrefixGroup1);
			//Repne = new OpCode(0xF2, 1, OpCodeForm.Normal, Mnemonic.repne, OpCodeType.PrefixGroup1);
			//Repe = new OpCode(0xF3, 1, OpCodeForm.Normal, Mnemonic.repe, OpCodeType.PrefixGroup1);

			//CsSegmentOverride = new OpCode(0x2E, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.CsSegmentOverride);
			//SsSegmentOverride = new OpCode(0x36, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.CsSegmentOverride);
			//DsSegmentOverride = new OpCode(0x3E, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.CsSegmentOverride);
			//EsSegmentOverride = new OpCode(0x26, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.CsSegmentOverride);
			//FsSegmentOverride = new OpCode(0x64, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.CsSegmentOverride);
			//GsSegmentOverride = new OpCode(0x65, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.CsSegmentOverride);

			//BranchNotTaken = new OpCode(0x2E, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.BranchNotTakenHint);
			//BranchTaken = new OpCode(0x3E, Mnemonic.Null, OpCodeType.PrefixGroup2, Ia32Prefixes.BranchTakenHint);

			//OperandSizeOverride = new OpCode(0x66, Mnemonic.Null, OpCodeType.PrefixGroup3, Ia32Prefixes.OperandSizeOverride);
			//AddressSizeOverride = new OpCode(0x67, Mnemonic.Null, OpCodeType.PrefixGroup4, Ia32Prefixes.AddressSizeOverride);

			Lock = 0xF0;
			Repne = 0xF2;
			Repe = 0xF3;

			CsSegmentOverride = 0x2E;
			SsSegmentOverride = 0x36;
			DsSegmentOverride = 0x3E;
			EsSegmentOverride = 0x26;
			FsSegmentOverride = 0x64;
			GsSegmentOverride = 0x65;

			BranchNotTaken = 0x2E;
			BranchTaken = 0x3E;

			OperandSizeOverride = 0x66;
			AddressSizeOverride = 0x67;
		}

		protected IaOpCodeLookup()
		{
			Dictionary<int, Prefixes> prefixTable = new Dictionary<int, Prefixes>();
			this.DefinePrefixes(prefixTable);
			this._prefixTable = prefixTable;

			List<OpCode> simpleOpCodes = new List<OpCode>();
			this.DefineSimpleOpCodes(simpleOpCodes);
			this._simpleOpCodes = simpleOpCodes.ToArray();
			List<ExtendedOpCodeGroup> groups = new List<ExtendedOpCodeGroup>();
			this.DefineExtendedOpCodeGroups(groups);
			this._extendedGroups = groups;
		}

		#region OpCodes
		public OpCode GetMatchingOpCode(int value, int size)
		{
			List<OpCode> matchingOpcodes = new List<OpCode>();
			int normalValue = (value & ~0x07);
			foreach (var opcode in _simpleOpCodes)
			{
				if (
					(opcode.Length == size)
					&& (
						(opcode.Value == value)
						|| (
							(opcode.Form == OpCodeForm.RegCode)
							&& (opcode.Value == normalValue)
							)
							)
						)
				{
					return opcode;
				}
			}

			OpCode unknownOp = new OpCode(value, 0, OpCodeForm.Normal, Mnemonic.Unknown);
			return unknownOp;
		}

		#region Simple
		private OpCode[] _simpleOpCodes;
		protected virtual void DefineSimpleOpCodes(List<OpCode> opCodes)
		{
			opCodes.AddRange(new OpCode[] {
				// 0x
				new OpCode(0x00, 1, OpCodeForm.ModRM, Mnemonic.add, OperandType.Eb, OperandType.Gb),
				new OpCode(0x01, 1, OpCodeForm.ModRM, Mnemonic.add, OperandType.Ev, OperandType.Gv),
				new OpCode(0x02, 1, OpCodeForm.ModRM, Mnemonic.add, OperandType.Gb, OperandType.Eb),
				new OpCode(0x03, 1, OpCodeForm.ModRM, Mnemonic.add, OperandType.Gv, OperandType.Ev),
				new OpCode(0x04, 1, OpCodeForm.Normal, Mnemonic.add, OperandType.AL, OperandType.Ib),
				new OpCode(0x05, 1, OpCodeForm.Normal, Mnemonic.add, OperandType.rAX, OperandType.Iz),
				new OpCode(0x08, 1, OpCodeForm.ModRM, Mnemonic.or, OperandType.Eb, OperandType.Gb),
				new OpCode(0x09, 1, OpCodeForm.ModRM, Mnemonic.or, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0A, 1, OpCodeForm.ModRM, Mnemonic.or, OperandType.Gb, OperandType.Eb),
				new OpCode(0x0B, 1, OpCodeForm.ModRM, Mnemonic.or, OperandType.Gv,OperandType.Ev),
				new OpCode(0x0C, 1, OpCodeForm.Normal, Mnemonic.or, OperandType.AL, OperandType.Ib),
				new OpCode(0x0D, 1, OpCodeForm.Normal, Mnemonic.or, OperandType.rAX, OperandType.Iz),
				//   0F = escape code
				// 1x
				new OpCode(0x10, 1, OpCodeForm.ModRM, Mnemonic.adc, OperandType.Eb, OperandType.Gb),
				new OpCode(0x11, 1, OpCodeForm.ModRM, Mnemonic.adc, OperandType.Ev, OperandType.Gv),
				new OpCode(0x12, 1, OpCodeForm.ModRM, Mnemonic.adc, OperandType.Gb, OperandType.Eb),
				new OpCode(0x13, 1, OpCodeForm.ModRM, Mnemonic.adc, OperandType.Gv, OperandType.Ev),
				new OpCode(0x14, 1, OpCodeForm.Normal, Mnemonic.adc, OperandType.AL, OperandType.Ib),
				new OpCode(0x15, 1, OpCodeForm.ModRM, Mnemonic.adc, OperandType.Eb, OperandType.Gb),
				new OpCode(0x18, 1, OpCodeForm.ModRM, Mnemonic.sbb, OperandType.Eb, OperandType.Gb),
				new OpCode(0x19, 1, OpCodeForm.ModRM, Mnemonic.sbb, OperandType.Ev, OperandType.Gv),
				new OpCode(0x1A, 1, OpCodeForm.ModRM, Mnemonic.sbb, OperandType.Gb, OperandType.Eb),
				new OpCode(0x1B, 1, OpCodeForm.ModRM, Mnemonic.sbb, OperandType.Gv,OperandType.Ev),
				new OpCode(0x1C, 1, OpCodeForm.Normal, Mnemonic.sbb, OperandType.AL, OperandType.Ib),
				new OpCode(0x1D, 1, OpCodeForm.Normal, Mnemonic.sbb, OperandType.rAX, OperandType.Iz),
				// 2x
				new OpCode(0x20, 1, OpCodeForm.ModRM, Mnemonic.and, OperandType.Eb, OperandType.Gb),
				new OpCode(0x21, 1, OpCodeForm.ModRM, Mnemonic.and, OperandType.Ev, OperandType.Gv),
				new OpCode(0x22, 1, OpCodeForm.ModRM, Mnemonic.and, OperandType.Gb, OperandType.Eb),
				new OpCode(0x23, 1, OpCodeForm.ModRM, Mnemonic.and, OperandType.Gv, OperandType.Ev),
				new OpCode(0x24, 1, OpCodeForm.Normal, Mnemonic.and, OperandType.AL, OperandType.Ib),
				new OpCode(0x25, 1, OpCodeForm.ModRM, Mnemonic.and, OperandType.rAX, OperandType.Iz),
				//  EsSegmentOverride,
				new OpCode(0x28, 1, OpCodeForm.ModRM, Mnemonic.sub, OperandType.Eb, OperandType.Gb),
				new OpCode(0x29, 1, OpCodeForm.ModRM, Mnemonic.sub, OperandType.Ev, OperandType.Gv),
				new OpCode(0x2A, 1, OpCodeForm.ModRM, Mnemonic.sub, OperandType.Gb, OperandType.Eb),
				new OpCode(0x2B, 1, OpCodeForm.ModRM, Mnemonic.sub, OperandType.Gv,OperandType.Ev),
				new OpCode(0x2C, 1, OpCodeForm.Normal, Mnemonic.sub, OperandType.AL, OperandType.Ib),
				new OpCode(0x2D, 1, OpCodeForm.Normal, Mnemonic.sub, OperandType.rAX, OperandType.Iz),
				//  CsSegmentOverride,
				// 3x
				new OpCode(0x30, 1, OpCodeForm.ModRM, Mnemonic.xor, OperandType.Eb, OperandType.Gb),
				new OpCode(0x31, 1, OpCodeForm.ModRM, Mnemonic.xor, OperandType.Ev, OperandType.Gv),
				new OpCode(0x32, 1, OpCodeForm.ModRM, Mnemonic.xor, OperandType.Gb, OperandType.Eb),
				new OpCode(0x33, 1, OpCodeForm.ModRM, Mnemonic.xor, OperandType.Gv, OperandType.Ev),
				new OpCode(0x34, 1, OpCodeForm.Normal, Mnemonic.xor, OperandType.AL, OperandType.Ib),
				new OpCode(0x35, 1, OpCodeForm.ModRM, Mnemonic.xor, OperandType.Eb, OperandType.Gb),
				//  SsSegmentOverride,
				new OpCode(0x38, 1, OpCodeForm.ModRM, Mnemonic.cmp, OperandType.Eb, OperandType.Gb),
				new OpCode(0x39, 1, OpCodeForm.ModRM, Mnemonic.cmp, OperandType.Ev, OperandType.Gv),
				new OpCode(0x3A, 1, OpCodeForm.ModRM, Mnemonic.cmp, OperandType.Gb, OperandType.Eb),
				new OpCode(0x3B, 1, OpCodeForm.ModRM, Mnemonic.cmp, OperandType.Gv,OperandType.Ev),
				new OpCode(0x3C, 1, OpCodeForm.Normal, Mnemonic.cmp, OperandType.AL, OperandType.Ib),
				new OpCode(0x3D, 1, OpCodeForm.Normal, Mnemonic.cmp, OperandType.rAX, OperandType.Iz),
				//  DsSegmentOverride,
				// 6x
				//  FsSegmentOverride,
				//  GsSegmentOverride,
				//  OperandSizeOverride,
				//  AddressSizeOverride,
				new OpCode(0x69, 1, OpCodeForm.ModRM, Mnemonic.imul, OperandType.Gv, OperandType.Ev, OperandType.Iz),
				new OpCode(0x6B, 1, OpCodeForm.ModRM, Mnemonic.imul, OperandType.Gv,OperandType.Ev,OperandType.Ib),
				new OpCode(0x6C, 1, OpCodeForm.Normal, Mnemonic.ins, OperandType.Yb, OperandType.DX),
				new OpCode(0x6D, 1, OpCodeForm.Normal, Mnemonic.ins, OperandType.Yz, OperandType.DX),
				new OpCode(0x6E, 1, OpCodeForm.Normal, Mnemonic.outs, OperandType.DX, OperandType.Xb),
				new OpCode(0x6F, 1, OpCodeForm.Normal, Mnemonic.outs, OperandType.DX, OperandType.Xz),
				// 7x
				new OpCode(0x70, 1, OpCodeForm.Normal, Mnemonic.jo, OperandType.Jb),
				new OpCode(0x71, 1, OpCodeForm.Normal, Mnemonic.jno, OperandType.Jb),
				new OpCode(0x72, 1, OpCodeForm.Normal, Mnemonic.jb, OperandType.Jb),
				new OpCode(0x73, 1, OpCodeForm.Normal, Mnemonic.jae, OperandType.Jb),
				new OpCode(0x74, 1, OpCodeForm.Normal, Mnemonic.je, OperandType.Jb),
				new OpCode(0x75, 1, OpCodeForm.Normal, Mnemonic.jne, OperandType.Jb),
				new OpCode(0x76, 1, OpCodeForm.Normal, Mnemonic.jbe, OperandType.Jb),
				new OpCode(0x77, 1, OpCodeForm.Normal, Mnemonic.ja, OperandType.Jb),
				new OpCode(0x78, 1, OpCodeForm.Normal, Mnemonic.js, OperandType.Jb),
				new OpCode(0x79, 1, OpCodeForm.Normal, Mnemonic.jns, OperandType.Jb),
				new OpCode(0x7A, 1, OpCodeForm.Normal, Mnemonic.jp, OperandType.Jb),
				new OpCode(0x7B, 1, OpCodeForm.Normal, Mnemonic.jnp, OperandType.Jb),
				new OpCode(0x7C, 1, OpCodeForm.Normal, Mnemonic.jl, OperandType.Jb),
				new OpCode(0x7D, 1, OpCodeForm.Normal, Mnemonic.jge, OperandType.Jb),
				new OpCode(0x7E, 1, OpCodeForm.Normal, Mnemonic.jle, OperandType.Jb),
				new OpCode(0x7F, 1, OpCodeForm.Normal, Mnemonic.jg, OperandType.Jb),
				// 8x
				//  80 - 84: Immediate Grp 1
				new OpCode(0x84, 1, OpCodeForm.ModRM, Mnemonic.test, OperandType.Eb, OperandType.Gb),
				new OpCode(0x85, 1, OpCodeForm.ModRM, Mnemonic.test, OperandType.Ev, OperandType.Gv),
				new OpCode(0x86, 1, OpCodeForm.ModRM, Mnemonic.xchg, OperandType.Eb, OperandType.Gb),
				new OpCode(0x87, 1, OpCodeForm.ModRM, Mnemonic.xchg, OperandType.Ev, OperandType.Gv),
				new OpCode(0x88, 1, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Eb, OperandType.Gb),
				new OpCode(0x89, 1, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Ev, OperandType.Gv),
				new OpCode(0x8A, 1, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Gb, OperandType.Eb),
				new OpCode(0x8B, 1, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Gv, OperandType.Ev),
				new OpCode(0x8C, 1, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Ev, OperandType.Sw),
				new OpCode(0x8D, 1, OpCodeForm.Normal, Mnemonic.lea, OperandType.Gv, OperandType.Mv),
				new OpCode(0x8E, 1, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Sw, OperandType.Ew),
				//  8F: Grp 1A
				// 9x
				new OpCode(0x90, 1, OpCodeForm.RegCode, Mnemonic.xchg, OperandType.rAX, OperandType.GeneralPurposeRegister | OperandType.OpCodeReg | OperandType.OperandWordDwordQword),
				new OpCode(0x98, 1, OpCodeForm.Normal, Mnemonic.cbw),
				new OpCode(0x99, 1, OpCodeForm.Normal, Mnemonic.cwd),
				new OpCode(0x9B, 1, OpCodeForm.Normal, Mnemonic.fwait),
				new OpCode(0x9C, 1, OpCodeForm.Normal, Mnemonic.pushf),
				new OpCode(0x9D, 1, OpCodeForm.Normal, Mnemonic.popf),
				new OpCode(0x9E, 1, OpCodeForm.Normal, Mnemonic.sahf),
				new OpCode(0x9F, 1, OpCodeForm.Normal, Mnemonic.lahf),
				// Ax
				new OpCode(0xA0, 1, OpCodeForm.Normal, Mnemonic.mov, OperandType.AL, OperandType.Ob),
				new OpCode(0xA1, 1, OpCodeForm.Normal, Mnemonic.mov, OperandType.rAX, OperandType.Ov),
				new OpCode(0xA2, 1, OpCodeForm.Normal, Mnemonic.mov, OperandType.Ob, OperandType.AL),
				new OpCode(0xA3, 1, OpCodeForm.Normal, Mnemonic.mov, OperandType.Ov, OperandType.rAX),
				new OpCode(0xA4, 1, OpCodeForm.Normal, Mnemonic.movs, OperandType.Xb, OperandType.Yb),
				new OpCode(0xA5, 1, OpCodeForm.Normal, Mnemonic.movs, OperandType.Xv, OperandType.Yv),
				new OpCode(0xA6, 1, OpCodeForm.Normal, Mnemonic.cmps, OperandType.Xb, OperandType.Yb),
				new OpCode(0xA7, 1, OpCodeForm.Normal, Mnemonic.cmps, OperandType.Xv, OperandType.Yv),
				new OpCode(0xA8, 1, OpCodeForm.Normal, Mnemonic.test, OperandType.AL, OperandType.Ib),
				new OpCode(0xA9, 1, OpCodeForm.Normal, Mnemonic.test, OperandType.rAX, OperandType.Iz),
				new OpCode(0xAA, 1, OpCodeForm.Normal, Mnemonic.stos, OperandType.AL),
				new OpCode(0xAB, 1, OpCodeForm.Normal, Mnemonic.stos, OperandType.Yv, OperandType.rAX),
				new OpCode(0xAC, 1, OpCodeForm.Normal, Mnemonic.lods, OperandType.AL, OperandType.Xb),
				new OpCode(0xAD, 1, OpCodeForm.Normal, Mnemonic.lods, OperandType.rAX, OperandType.Xv),
				new OpCode(0xAE, 1, OpCodeForm.Normal, Mnemonic.scas, OperandType.AL, OperandType.Yb),
				new OpCode(0xAF, 1, OpCodeForm.Normal, Mnemonic.scas, OperandType.rAX, OperandType.Xv),  // TODO: Is this correcT?
				// Bx
				new OpCode(0xB0, 1, OpCodeForm.RegCode, Mnemonic.mov, OperandType.OpCodeReg | OperandType.Byte | OperandType.GeneralPurposeRegister, OperandType.Ib),
				new OpCode(0xB8, 1, OpCodeForm.RegCode, Mnemonic.mov, OperandType.OpCodeReg | OperandType.OperandWordDwordQword | OperandType.GeneralPurposeRegister, OperandType.Iv),
				// Cx
				//  C0 - C1: Shift Group 2
				new OpCode(0xC2, 1, OpCodeForm.Normal, Mnemonic.ret, OperandType.Iw),
				new OpCode(0xC3, 1, OpCodeForm.Normal, Mnemonic.ret),
				//  C6 - C7: Group 11
				new OpCode(0xC8, 1, OpCodeForm.Normal, Mnemonic.enter, OperandType.Iw, OperandType.Ib),
				new OpCode(0xC9, 1, OpCodeForm.Normal, Mnemonic.leave),
				new OpCode(0xCA, 1, OpCodeForm.Normal, Mnemonic.retf, OperandType.Iw),
				new OpCode(0xCB, 1, OpCodeForm.Normal, Mnemonic.retf),
				new OpCode(0xCC, 1, OpCodeForm.Normal, Mnemonic.@int3),
				new OpCode(0xCD, 1, OpCodeForm.Normal, Mnemonic.@int, OperandType.Ib),
				new OpCode(0xCF, 1, OpCodeForm.Normal, Mnemonic.iret),
				// Dx
				//  D0-D3: Shift Group 2
				//  D6: Undefined
				new OpCode(0xD7, 1, OpCodeForm.Normal, Mnemonic.xlatb),
				//  D8-D7: Escape to coprocessor instruction set
				// Ex
				new OpCode(0xE0, 1, OpCodeForm.Normal, Mnemonic.loopne, OperandType.Jb),
				new OpCode(0xE1, 1, OpCodeForm.Normal, Mnemonic.loope, OperandType.Jb),
				new OpCode(0xE2, 1, OpCodeForm.Normal, Mnemonic.loop, OperandType.Jb),
				new OpCode(0xE3, 1, OpCodeForm.Normal, Mnemonic.jcxz, OperandType.Jb),
				new OpCode(0xE4, 1, OpCodeForm.Normal, Mnemonic.@in, OperandType.AL, OperandType.Ib),
				new OpCode(0xE5, 1, OpCodeForm.Normal, Mnemonic.@in, OperandType.eAX, OperandType.Ib),
				new OpCode(0xE6, 1, OpCodeForm.Normal, Mnemonic.@out, OperandType.Ib, OperandType.AL),
				new OpCode(0xE7, 1, OpCodeForm.Normal, Mnemonic.@out, OperandType.Ib, OperandType.eAX),
				new OpCode(0xE8, 1, OpCodeForm.Normal, Mnemonic.call, OperandType.Jz),
				new OpCode(0xE9, 1, OpCodeForm.Normal, Mnemonic.jmp, OperandType.Jz),
				new OpCode(0xEB, 1, OpCodeForm.Normal, Mnemonic.jmp, OperandType.Jb),
				new OpCode(0xEC, 1, OpCodeForm.Normal, Mnemonic.@in, OperandType.AL, OperandType.DX),
				new OpCode(0xED, 1, OpCodeForm.Normal, Mnemonic.@in, OperandType.eAX, OperandType.DX),
				new OpCode(0xEE, 1, OpCodeForm.Normal, Mnemonic.@out, OperandType.DX, OperandType.AL),
				new OpCode(0xEF, 1, OpCodeForm.Normal, Mnemonic.@out, OperandType.DX, OperandType.eAX),
				// Fx
				//  Lock,
				//  F1: Undefined
				//  Repne,
				//  Repe,
				new OpCode(0xF4, 1, OpCodeForm.Normal, Mnemonic.hlt),
				new OpCode(0xF5, 1, OpCodeForm.Normal, Mnemonic.cmc),
				//  F6-F7: Unary Group 3
				new OpCode(0xF8, 1, OpCodeForm.Normal, Mnemonic.clc),
				new OpCode(0xF9, 1, OpCodeForm.Normal, Mnemonic.stc),
				new OpCode(0xFA, 1, OpCodeForm.Normal, Mnemonic.cli),
				new OpCode(0xFB, 1, OpCodeForm.Normal, Mnemonic.sti),
				new OpCode(0xFC, 1, OpCodeForm.Normal, Mnemonic.cld),
				new OpCode(0xFD, 1, OpCodeForm.Normal, Mnemonic.std),
				//  FE-FF: INC/DEC Group 4/5
				// 0F0x
				//  0F00: Group 6
				//  0F01: Group 7
				new OpCode(0x0F02, 2, OpCodeForm.ModRM, Mnemonic.lar, OperandType.Gv, OperandType.Ew),
				new OpCode(0x0F03, 2, OpCodeForm.ModRM, Mnemonic.lsl, OperandType.Gv, OperandType.Ew),
				new OpCode(0x0F05, 2, OpCodeForm.Normal, Mnemonic.syscall),
				new OpCode(0x0F06, 2, OpCodeForm.Normal, Mnemonic.clts),
				new OpCode(0x0F07, 2, OpCodeForm.Normal, Mnemonic.sysret),
				new OpCode(0x0F08, 2, OpCodeForm.Normal, Mnemonic.invd),
				new OpCode(0x0F09, 2, OpCodeForm.Normal, Mnemonic.wbinvd),
				new OpCode(0x0F0B, 2, OpCodeForm.Normal, Mnemonic.ud2),
				new OpCode(0x0F0D, 2, OpCodeForm.ModRM, Mnemonic.nop, OperandType.Ev),
				// 0F1x
				new OpCode(0x0F10, 2, OpCodeForm.Normal, Mnemonic.movups, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F11, 2, OpCodeForm.Normal, Mnemonic.movups, OperandType.Wps, OperandType.Vps),
				new OpCode(0x0F12, 2, OpCodeForm.Normal, Mnemonic.movlps, OperandType.Vq, OperandType.Mq),
				new OpCode(0x0F13, 2, OpCodeForm.Normal, Mnemonic.movlps, OperandType.Mq, OperandType.Vq),
				new OpCode(0x0F14, 2, OpCodeForm.Normal, Mnemonic.unpcklps, OperandType.Vps, OperandType.Wq),
				new OpCode(0x0F15, 2, OpCodeForm.Normal, Mnemonic.unpckhps, OperandType.Vps, OperandType.Wq),
				new OpCode(0x0F16, 2, OpCodeForm.Normal, Mnemonic.movhps, OperandType.Vq, OperandType.Mq),
				new OpCode(0x0F17, 2, OpCodeForm.Normal, Mnemonic.movhps, OperandType.Mq, OperandType.Vq),
				//  0F08: Group 16
				new OpCode(0x0F1F, 2, OpCodeForm.ModRM, Mnemonic.nop, OperandType.Ev),
				// 0F2x
				new OpCode(0x0F20, 2, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Rd, OperandType.Cd),
				new OpCode(0x0F21, 2, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Rd, OperandType.Dd),
				new OpCode(0x0F22, 2, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Cd, OperandType.Rd),
				new OpCode(0x0F23, 2, OpCodeForm.ModRM, Mnemonic.mov, OperandType.Dd, OperandType.Rd),
				new OpCode(0x0F28, 2, OpCodeForm.Normal, Mnemonic.movaps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F29, 2, OpCodeForm.Normal, Mnemonic.movaps, OperandType.Wps, OperandType.Vps),
				new OpCode(0x0F2A, 2, OpCodeForm.Normal, Mnemonic.cvtpi2ps, OperandType.Vps, OperandType.Qq),
				new OpCode(0x0F2B, 2, OpCodeForm.Normal, Mnemonic.movntps, OperandType.Mps, OperandType.Vps),
				new OpCode(0x0F2C, 2, OpCodeForm.Normal, Mnemonic.cvttps2pi, OperandType.Qq, OperandType.Wps),
				new OpCode(0x0F2D, 2, OpCodeForm.Normal, Mnemonic.cvtps2pi, OperandType.Qq, OperandType.Wps),
				new OpCode(0x0F2E, 2, OpCodeForm.Normal, Mnemonic.ucomiss, OperandType.Vss, OperandType.Wss),
				new OpCode(0x0F2F, 2, OpCodeForm.Normal, Mnemonic.comiss, OperandType.Vps, OperandType.Wps),
				// 0F3x
				new OpCode(0x0F30, 2, OpCodeForm.Normal, Mnemonic.wrmsr),
				new OpCode(0x0F31, 2, OpCodeForm.Normal, Mnemonic.rdtsc),
				new OpCode(0x0F32, 2, OpCodeForm.Normal, Mnemonic.rdmsr),
				new OpCode(0x0F33, 2, OpCodeForm.Normal, Mnemonic.rdpmc),
				new OpCode(0x0F34, 2, OpCodeForm.Normal, Mnemonic.sysenter),
				new OpCode(0x0F35, 2, OpCodeForm.Normal, Mnemonic.sysexit),
				//  0F38: Escape
				//  0F3A: Escape
				// 0F4x
				new OpCode(0x0F40, 2, OpCodeForm.ModRM, Mnemonic.cmovo, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F41, 2, OpCodeForm.ModRM, Mnemonic.cmovno, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F42, 2, OpCodeForm.ModRM, Mnemonic.cmovb, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F43, 2, OpCodeForm.ModRM, Mnemonic.cmovae, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F44, 2, OpCodeForm.ModRM, Mnemonic.cmove, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F45, 2, OpCodeForm.ModRM, Mnemonic.cmovne, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F46, 2, OpCodeForm.ModRM, Mnemonic.cmovbe, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F47, 2, OpCodeForm.ModRM, Mnemonic.cmova, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F48, 2, OpCodeForm.ModRM, Mnemonic.cmovs, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F49, 2, OpCodeForm.ModRM, Mnemonic.cmovns, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F4A, 2, OpCodeForm.ModRM, Mnemonic.cmovp, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F4B, 2, OpCodeForm.ModRM, Mnemonic.cmovnp, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F4C, 2, OpCodeForm.ModRM, Mnemonic.cmovl, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F4D, 2, OpCodeForm.ModRM, Mnemonic.cmovge, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F4E, 2, OpCodeForm.ModRM, Mnemonic.cmovle, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0F4F, 2, OpCodeForm.ModRM, Mnemonic.cmovg, OperandType.Gv, OperandType.Ev),
				// 0F5x
				new OpCode(0x0F50, 2, OpCodeForm.Normal, Mnemonic.movmskps, OperandType.Gd, OperandType.Ups),
				new OpCode(0x0F51, 2, OpCodeForm.Normal, Mnemonic.sqrtps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F52, 2, OpCodeForm.Normal, Mnemonic.rsqrtps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F53, 2, OpCodeForm.Normal, Mnemonic.rcpps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F54, 2, OpCodeForm.Normal, Mnemonic.andps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F55, 2, OpCodeForm.Normal, Mnemonic.andnps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F56, 2, OpCodeForm.Normal, Mnemonic.orps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F57, 2, OpCodeForm.Normal, Mnemonic.xorps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F58, 2, OpCodeForm.Normal, Mnemonic.addps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F59, 2, OpCodeForm.Normal, Mnemonic.mulps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F5A, 2, OpCodeForm.Normal, Mnemonic.cvtps2pd, OperandType.Vpd, OperandType.Wpd),
				new OpCode(0x0F5B, 2, OpCodeForm.Normal, Mnemonic.cvtdq2ps, OperandType.Vps, OperandType.Wpq),
				new OpCode(0x0F5C, 2, OpCodeForm.Normal, Mnemonic.subps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F5D, 2, OpCodeForm.Normal, Mnemonic.minps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F5E, 2, OpCodeForm.Normal, Mnemonic.divps, OperandType.Vps, OperandType.Wps),
				new OpCode(0x0F5F, 2, OpCodeForm.Normal, Mnemonic.maxps, OperandType.Vps, OperandType.Wps),
				// 0F6x
				new OpCode(0x0F60, 2, OpCodeForm.Normal, Mnemonic.punpcklbw, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F61, 2, OpCodeForm.Normal, Mnemonic.punpcklwd, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F62, 2, OpCodeForm.Normal, Mnemonic.punpckldq, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F63, 2, OpCodeForm.Normal, Mnemonic.packsswd, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F64, 2, OpCodeForm.Normal, Mnemonic.pcmpgtb, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F65, 2, OpCodeForm.Normal, Mnemonic.pcmpgtw, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F66, 2, OpCodeForm.Normal, Mnemonic.pcmpgtd, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F67, 2, OpCodeForm.Normal, Mnemonic.packuswb, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F68, 2, OpCodeForm.Normal, Mnemonic.punpckhbw, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F69, 2, OpCodeForm.Normal, Mnemonic.punpckhwd, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F6A, 2, OpCodeForm.Normal, Mnemonic.punpckhdq, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F6B, 2, OpCodeForm.Normal, Mnemonic.packssdw, OperandType.Pq, OperandType.Qd),
				new OpCode(0x0F6C, 2, OpCodeForm.Normal, Mnemonic.punpcklqdq, OperandType.Vdq, OperandType.Wdq),
				new OpCode(0x0F6D, 2, OpCodeForm.Normal, Mnemonic.punpckhqdq, OperandType.Vdq, OperandType.Wdq),
				new OpCode(0x0F6E, 2, OpCodeForm.Normal, Mnemonic.mov, OperandType.Pd, OperandType.Ed),
				new OpCode(0x0F6F, 2, OpCodeForm.Normal, Mnemonic.mov, OperandType.Pq, OperandType.Qq),
				// 0F7x
				new OpCode(0x0F70, 2, OpCodeForm.Normal, Mnemonic.pshufw, OperandType.Pq, OperandType.Qq, OperandType.Ib ),
				//  0F71: Group 12
				//  0F72: Group 13
				//  0F73: Group 14
				new OpCode(0x0F74, 2, OpCodeForm.Normal, Mnemonic.pcmpeqb, OperandType.Pq, OperandType.Qq ),
				new OpCode(0x0F75, 2, OpCodeForm.Normal, Mnemonic.pcmpeqw, OperandType.Pq, OperandType.Qq ),
				new OpCode(0x0F76, 2, OpCodeForm.Normal, Mnemonic.pcmpeqd, OperandType.Pq, OperandType.Qq ),
				new OpCode(0x0F77, 2, OpCodeForm.Normal, Mnemonic.emms ),
				new OpCode(0x0F78, 2, OpCodeForm.Normal, Mnemonic.vmread, OperandType.Ed, OperandType.Gd ),
				new OpCode(0x0F79, 2, OpCodeForm.Normal, Mnemonic.vmwrite, OperandType.Ed, OperandType.Gd ),
				//  0F7A: Undefined
				//  0F7B: Undefined
				new OpCode(0x0F7C, 2, OpCodeForm.Normal, Mnemonic.haddps, OperandType.Vps, OperandType.Wps ),
				new OpCode(0x0F7D, 2, OpCodeForm.Normal, Mnemonic.hsubps, OperandType.Vps, OperandType.Wps ),
				new OpCode(0x0F7E, 2, OpCodeForm.Normal, Mnemonic.mov, OperandType.Ed, OperandType.Pd ),
				new OpCode(0x0F7F, 2, OpCodeForm.Normal, Mnemonic.mov, OperandType.Qq, OperandType.Pq ),
				// 0F8x
				new OpCode(0x0F80, 2, OpCodeForm.Normal, Mnemonic.jo, OperandType.Jz),
				new OpCode(0x0F81, 2, OpCodeForm.Normal, Mnemonic.jno, OperandType.Jz),
				new OpCode(0x0F82, 2, OpCodeForm.Normal, Mnemonic.jb, OperandType.Jz),
				new OpCode(0x0F83, 2, OpCodeForm.Normal, Mnemonic.jae, OperandType.Jz),
				new OpCode(0x0F84, 2, OpCodeForm.Normal, Mnemonic.je, OperandType.Jz),
				new OpCode(0x0F85, 2, OpCodeForm.Normal, Mnemonic.jne, OperandType.Jz),
				new OpCode(0x0F86, 2, OpCodeForm.Normal, Mnemonic.jbe, OperandType.Jz),
				new OpCode(0x0F87, 2, OpCodeForm.Normal, Mnemonic.ja, OperandType.Jz),
				new OpCode(0x0F88, 2, OpCodeForm.Normal, Mnemonic.js, OperandType.Jz),
				new OpCode(0x0F89, 2, OpCodeForm.Normal, Mnemonic.jns, OperandType.Jz),
				new OpCode(0x0F8A, 2, OpCodeForm.Normal, Mnemonic.jp, OperandType.Jz),
				new OpCode(0x0F8B, 2, OpCodeForm.Normal, Mnemonic.jnp, OperandType.Jz),
				new OpCode(0x0F8C, 2, OpCodeForm.Normal, Mnemonic.jl, OperandType.Jz),
				new OpCode(0x0F8D, 2, OpCodeForm.Normal, Mnemonic.jge, OperandType.Jz),
				new OpCode(0x0F8E, 2, OpCodeForm.Normal, Mnemonic.jle, OperandType.Jz),
				new OpCode(0x0F8F, 2, OpCodeForm.Normal, Mnemonic.jg, OperandType.Jz),
				// 0F9x
				new OpCode(0x0F90, 2, OpCodeForm.Normal, Mnemonic.seto, OperandType.Eb),
				new OpCode(0x0F91, 2, OpCodeForm.Normal, Mnemonic.setno, OperandType.Eb),
				new OpCode(0x0F92, 2, OpCodeForm.Normal, Mnemonic.setb, OperandType.Eb),
				new OpCode(0x0F93, 2, OpCodeForm.Normal, Mnemonic.setae, OperandType.Eb),
				new OpCode(0x0F94, 2, OpCodeForm.Normal, Mnemonic.sete, OperandType.Eb),
				new OpCode(0x0F95, 2, OpCodeForm.Normal, Mnemonic.setne, OperandType.Eb),
				new OpCode(0x0F96, 2, OpCodeForm.Normal, Mnemonic.setbe, OperandType.Eb),
				new OpCode(0x0F97, 2, OpCodeForm.Normal, Mnemonic.seta, OperandType.Eb),
				new OpCode(0x0F98, 2, OpCodeForm.Normal, Mnemonic.sets, OperandType.Eb),
				new OpCode(0x0F99, 2, OpCodeForm.Normal, Mnemonic.setns, OperandType.Eb),
				new OpCode(0x0F9A, 2, OpCodeForm.Normal, Mnemonic.setp, OperandType.Eb),
				new OpCode(0x0F9B, 2, OpCodeForm.Normal, Mnemonic.setnp, OperandType.Eb),
				new OpCode(0x0F9C, 2, OpCodeForm.Normal, Mnemonic.setl, OperandType.Eb),
				new OpCode(0x0F9D, 2, OpCodeForm.Normal, Mnemonic.setge, OperandType.Eb),
				new OpCode(0x0F9E, 2, OpCodeForm.Normal, Mnemonic.setle, OperandType.Eb),
				new OpCode(0x0F9F, 2, OpCodeForm.Normal, Mnemonic.setg, OperandType.Eb),
				// 0FAx
				new OpCode(0x0FA0, 2, OpCodeForm.Normal, Mnemonic.push, OperandType.FS),
				new OpCode(0x0FA1, 2, OpCodeForm.Normal, Mnemonic.pop, OperandType.FS),
				new OpCode(0x0FA2, 2, OpCodeForm.Normal, Mnemonic.cpuid),
				new OpCode(0x0FA3, 2, OpCodeForm.Normal, Mnemonic.bt, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0F44, 2, OpCodeForm.Normal, Mnemonic.shld, OperandType.Ev, OperandType.Gv, OperandType.Ib),
				new OpCode(0x0F45, 2, OpCodeForm.Normal, Mnemonic.shld, OperandType.Ev, OperandType.Gv, OperandType.CL),
				//  0FA6: Undefined
				//  0FA7: Undefined
				new OpCode(0x0FA8, 2, OpCodeForm.Normal, Mnemonic.push, OperandType.GS),
				new OpCode(0x0FA9, 2, OpCodeForm.Normal, Mnemonic.pop, OperandType.GS),
				new OpCode(0x0FAA, 2, OpCodeForm.Normal, Mnemonic.rsm),
				new OpCode(0x0FAB, 2, OpCodeForm.Normal, Mnemonic.bts, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0FAC, 2, OpCodeForm.Normal, Mnemonic.shrd, OperandType.Ev, OperandType.Gv, OperandType.Ib),
				new OpCode(0x0FAD, 2, OpCodeForm.Normal, Mnemonic.shrd, OperandType.Ev, OperandType.Gv, OperandType.CL),
				//  0FAE: Group 15
				new OpCode(0x0FAF, 2, OpCodeForm.Normal, Mnemonic.imul, OperandType.Ev, OperandType.Gv),
				// 0FBx
				new OpCode(0x0FB0, 2, OpCodeForm.Normal, Mnemonic.cmpxchg, OperandType.Eb, OperandType.Gb),
				new OpCode(0x0FB1, 2, OpCodeForm.Normal, Mnemonic.cmpxchg, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0FB2, 2, OpCodeForm.Normal, Mnemonic.lss, OperandType.Gv, OperandType.Mp),
				new OpCode(0x0FB3, 2, OpCodeForm.Normal, Mnemonic.btr, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0FB4, 2, OpCodeForm.Normal, Mnemonic.lfs, OperandType.Gv, OperandType.Mp),
				new OpCode(0x0FB5, 2, OpCodeForm.Normal, Mnemonic.lgs, OperandType.Gv, OperandType.Mp),
				new OpCode(0x0FB6, 2, OpCodeForm.Normal, Mnemonic.movzx, OperandType.Gv, OperandType.Eb),
				new OpCode(0x0FB7, 2, OpCodeForm.Normal, Mnemonic.movzx, OperandType.Gv, OperandType.Ew),
				//  0FB8: jmpe (IPF)
				//  0FB9: Group 10
				//  0FBA: Group 8
				new OpCode(0x0FBB, 2, OpCodeForm.Normal, Mnemonic.btc, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0FBC, 2, OpCodeForm.Normal, Mnemonic.bsf, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0FBD, 2, OpCodeForm.Normal, Mnemonic.bsr, OperandType.Gv, OperandType.Ev),
				new OpCode(0x0FBE, 2, OpCodeForm.Normal, Mnemonic.movsx, OperandType.Gv, OperandType.Eb),
				new OpCode(0x0FBF, 2, OpCodeForm.Normal, Mnemonic.movsx, OperandType.Gv, OperandType.Ew),
				// 0FCx
				new OpCode(0x0FC0, 2, OpCodeForm.Normal, Mnemonic.xadd, OperandType.Eb, OperandType.Gb),
				new OpCode(0x0FC1, 2, OpCodeForm.Normal, Mnemonic.xadd, OperandType.Ev, OperandType.Gv),
				new OpCode(0x0FC2, 2, OpCodeForm.Normal, Mnemonic.cmpps, OperandType.Vps, OperandType.Wps, OperandType.Ib),
				new OpCode(0x0FC3, 2, OpCodeForm.Normal, Mnemonic.movnti, OperandType.Md, OperandType.Gd),
				new OpCode(0x0FC4, 2, OpCodeForm.Normal, Mnemonic.pinsrw, OperandType.Pq, OperandType.Ew, OperandType.Ib),
			});
		}
		#endregion
		#region Extended
		private IEnumerable<ExtendedOpCodeGroup> _extendedGroups;
		protected virtual void DefineExtendedOpCodeGroups(List<ExtendedOpCodeGroup> groups)
		{
			ExtendedOpCodeGroup group1 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.add, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.or, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.adc, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.sbb, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.and, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.sub, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.xor, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.cmp, ModRM.FromNnn(7))
				);
			group1.OperandTypesByOpCode[0x80] = new OperandType[] { OperandType.Eb, OperandType.Ib };
			group1.OperandTypesByOpCode[0x81] = new OperandType[] { OperandType.Ev, OperandType.Iz };
			group1.OperandTypesByOpCode[0x82] = new OperandType[] { OperandType.Eb, OperandType.Ib };
			group1.OperandTypesByOpCode[0x83] = new OperandType[] { OperandType.Ev, OperandType.Ib };

			ExtendedOpCodeGroup group1a = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.pop, ModRM.FromNnn(0))
				);
			group1a.OperandTypesByOpCode[0x8F] = new OperandType[] { OperandType.Ev };

			ExtendedOpCodeGroup group2 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.rol, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.ror, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.rcl, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.rcr, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.shl, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.shr, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.sar, ModRM.FromNnn(7))
				);
			group2.OperandTypesByOpCode[0xC0] = new OperandType[] { OperandType.Eb, OperandType.Ib };
			group2.OperandTypesByOpCode[0xC1] = new OperandType[] { OperandType.Ev, OperandType.Ib };
			group2.OperandTypesByOpCode[0xD0] = new OperandType[] { OperandType.Eb };
			group2.OperandTypesByOpCode[0xD1] = new OperandType[] { OperandType.Ev };
			group2.OperandTypesByOpCode[0xD2] = new OperandType[] { OperandType.Eb, OperandType.CL };
			group2.OperandTypesByOpCode[0xD3] = new OperandType[] { OperandType.Ev, OperandType.CL };

			ExtendedOpCodeGroup group3a = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.test, ModRM.FromNnn(0))
				);
			group3a.OperandTypesByOpCode[0xF6] = new OperandType[] { OperandType.Eb, OperandType.Ib };
			group3a.OperandTypesByOpCode[0xF7] = new OperandType[] { OperandType.Ev, OperandType.Iz };

			ExtendedOpCodeGroup group3b = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.not, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.neg, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.mul, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.imul, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.div, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.idiv, ModRM.FromNnn(7))
				);
			group3b.OperandTypesByOpCode[0xF6] = new OperandType[] { OperandType.Eb };
			group3b.OperandTypesByOpCode[0xF7] = new OperandType[] { OperandType.Ev };

			//ExtendedOpCodeGroup group3c = new ExtendedOpCodeGroup();
			//group3c.OperandTypesByOpCode[0xF6] = new OperandType[] { OperandType.AL };
			//group3c.OperandTypesByOpCode[0xF7] = new OperandType[] { OperandType.rAX };

			ExtendedOpCodeGroup group4 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.inc, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.dec, ModRM.FromNnn(1))
				);
			group4.OperandTypesByOpCode[0xFE] = new OperandType[] { OperandType.Eb };

			ExtendedOpCodeGroup group5a = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.inc, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.dec, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.call, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.jmp, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.push, ModRM.FromNnn(6))
				);
			group5a.OperandTypesByOpCode[0xFF] = new OperandType[] { OperandType.Ev };

			ExtendedOpCodeGroup group5b = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.callf, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.jmpf, ModRM.FromNnn(5))
				);
			group5b.OperandTypesByOpCode[0xFF] = new OperandType[] { OperandType.Ep };

			ExtendedOpCodeGroup group6 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.sldt, ModRM.FromNnn(0x00)),
				new ExtendedOpCode(Mnemonic.str, ModRM.FromNnn(0x01)),
				new ExtendedOpCode(Mnemonic.lldt, ModRM.FromNnn(0x02)),
				new ExtendedOpCode(Mnemonic.ltr, ModRM.FromNnn(0x03)),
				new ExtendedOpCode(Mnemonic.verr, ModRM.FromNnn(0x04)),
				new ExtendedOpCode(Mnemonic.verw, ModRM.FromNnn(0x05))
				);
			group6.OperandTypesByOpCode[0x0F00] = new OperandType[] { OperandType.Rv };

			ExtendedOpCodeGroup group7a = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.vmcall, ModRM.FromModRMNnn(3, 1, 0), true, true),
				new ExtendedOpCode(Mnemonic.vmlaunch, ModRM.FromModRMNnn(3, 2, 0), true, true),
				new ExtendedOpCode(Mnemonic.vmresume, ModRM.FromModRMNnn(3, 3, 0), true, true),
				new ExtendedOpCode(Mnemonic.vmxoff, ModRM.FromModRMNnn(3, 4, 0), true, true),
				new ExtendedOpCode(Mnemonic.monitor, ModRM.FromModRMNnn(3, 0, 1), true, true),
				new ExtendedOpCode(Mnemonic.mwait, ModRM.FromModRMNnn(3, 1, 1), true, true),
				new ExtendedOpCode(Mnemonic.swapgs, ModRM.FromModRMNnn(3, 0, 7), true, true)
				);
			group7a.OperandTypesByOpCode[0x0F01] = new OperandType[0];

			ExtendedOpCodeGroup group7b = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.sgdt, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.sidt, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.lgdt, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.lidt, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.smsw, ModRM.FromNnn(4), operandTypes: new OperandType[] { OperandType.Mw }),
				new ExtendedOpCode(Mnemonic.lmsw, ModRM.FromNnn(6), operandTypes: new OperandType[] { OperandType.Ew }),
				new ExtendedOpCode(Mnemonic.invlpg, ModRM.FromNnn(6), operandTypes: new OperandType[] { OperandType.Mb })
				);
			group7b.OperandTypesByOpCode[0x0F01] = new OperandType[] { OperandType.Ms };

			ExtendedOpCodeGroup group8 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.bt, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.bts, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.btr, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.btc, ModRM.FromNnn(7))
				);
			group8.OperandTypesByOpCode[0x0FBA] = new OperandType[] { OperandType.Ex, OperandType.Ib };

			ExtendedOpCodeGroup group9 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.cmpxch8b, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.vmclear, ModRM.FromNnn(6), mandatoryPrefixes: Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.vmxon, ModRM.FromNnn(6), mandatoryPrefixes: Prefixes.Repe),
				new ExtendedOpCode(Mnemonic.vmptrld, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.vmptrst, ModRM.FromNnn(7))
				);
			group9.OperandTypesByOpCode[0x0FC7] = new OperandType[] { OperandType.Mq };

			ExtendedOpCodeGroup group11 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.mov, ModRM.FromNnn(0))
				);
			group11.OperandTypesByOpCode[0xC6] = new OperandType[] { OperandType.Eb, OperandType.Ib };
			group11.OperandTypesByOpCode[0xC7] = new OperandType[] { OperandType.Ev, OperandType.Iv };

			ExtendedOpCodeGroup group12 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.psrlw, ModRM.FromModRMNnn(3, 0, 2), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psrlw, ModRM.FromModRMNnn(3, 0, 2), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib }),
				new ExtendedOpCode(Mnemonic.psraw, ModRM.FromModRMNnn(3, 0, 4), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psraw, ModRM.FromModRMNnn(3, 0, 4), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib }),
				new ExtendedOpCode(Mnemonic.psllw, ModRM.FromModRMNnn(3, 0, 6), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psllw, ModRM.FromModRMNnn(3, 0, 6), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib })
				);
			group12.OperandTypesByOpCode[0x0F71] = new OperandType[] { };

			ExtendedOpCodeGroup group13 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.psrld, ModRM.FromModRMNnn(3, 0, 2), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psrld, ModRM.FromModRMNnn(3, 0, 2), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib }),
				new ExtendedOpCode(Mnemonic.psrad, ModRM.FromModRMNnn(3, 0, 4), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psrad, ModRM.FromModRMNnn(3, 0, 4), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib }),
				new ExtendedOpCode(Mnemonic.pslld, ModRM.FromModRMNnn(3, 0, 6), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.pslld, ModRM.FromModRMNnn(3, 0, 6), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib })
				);
			group13.OperandTypesByOpCode[0x0F72] = new OperandType[] { };

			ExtendedOpCodeGroup group14 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.psrlq, ModRM.FromModRMNnn(3, 0, 2), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psrlq, ModRM.FromModRMNnn(3, 0, 2), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib }),
				new ExtendedOpCode(Mnemonic.psrldq, ModRM.FromModRMNnn(3, 0, 3), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psllq, ModRM.FromModRMNnn(3, 0, 6), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride),
				new ExtendedOpCode(Mnemonic.psllq, ModRM.FromModRMNnn(3, 0, 6), true, false, new OperandType[] { OperandType.Nq, OperandType.Ib }),
				new ExtendedOpCode(Mnemonic.pslldq, ModRM.FromModRMNnn(3, 0, 7), true, false, new OperandType[] { OperandType.Udq, OperandType.Ib }, Prefixes.OperandSizeOverride)
				);
			group14.OperandTypesByOpCode[0x0F73] = new OperandType[] { };

			ExtendedOpCodeGroup fpuGroupD8 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.fadd, new ModRM(0xC0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcom, new ModRM(0xD0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fsub, new ModRM(0xE0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fdiv, new ModRM(0xF0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fmul, new ModRM(0xC8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcomp, new ModRM(0xD8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fsubr, new ModRM(0xE8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fdivr, new ModRM(0xF8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),

				new ExtendedOpCode(Mnemonic.fadd, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.fmul, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.fcom, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.fcomp, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.fsub, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.fsubr, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.fdiv, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.fdivr, ModRM.FromNnn(7))
				);
			fpuGroupD8.OperandTypesByOpCode[0xD8] = new OperandType[] { OperandType.PackedSingle | OperandType.ModRM | OperandType.FpuOrAddress };

			ExtendedOpCodeGroup fpuGroupD9 = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.fld, new ModRM(0xC0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fnop, new ModRM(0xD0), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fchs, new ModRM(0xE0), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fabs, new ModRM(0xE1), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.ftst, new ModRM(0xE4), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fxam, new ModRM(0xE5), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.f2xm1, new ModRM(0xF0), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fyl2x, new ModRM(0xF1), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fptan, new ModRM(0xF2), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fpatan, new ModRM(0xF3), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fxtract, new ModRM(0xF4), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fprem1, new ModRM(0xF5), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fdecstp, new ModRM(0xF6), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fincstp, new ModRM(0xF7), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fxch, new ModRM(0xC8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fld1, new ModRM(0xE8), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fld2t, new ModRM(0xE9), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fld2e, new ModRM(0xEA), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fldp1, new ModRM(0xEB), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fldlg2, new ModRM(0xEC), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fldln2, new ModRM(0xED), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fldz, new ModRM(0xEE), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fprem, new ModRM(0xF8), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fyl2xp1, new ModRM(0xF9), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fsqrt, new ModRM(0xFA), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fsincos, new ModRM(0xFB), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.frndint, new ModRM(0xFC), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fscale, new ModRM(0xFD), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fsin, new ModRM(0xFE), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fcos, new ModRM(0xFF), true, true, operandTypes: new OperandType[0]),

				new ExtendedOpCode(Mnemonic.fld, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.fst, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.fstp, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.fldenv, ModRM.FromNnn(4), operandTypes: new OperandType[] { OperandType.Ev }),
				new ExtendedOpCode(Mnemonic.fldcw, ModRM.FromNnn(5), operandTypes: new OperandType[] { OperandType.Ew }),
				new ExtendedOpCode(Mnemonic.fstenv, ModRM.FromNnn(6), operandTypes: new OperandType[] { OperandType.Ev }),
				new ExtendedOpCode(Mnemonic.fstcw, ModRM.FromNnn(7), operandTypes: new OperandType[] { OperandType.Ew })
				);
			fpuGroupD9.OperandTypesByOpCode[0xD9] = new OperandType[] { OperandType.PackedSingle | OperandType.ModRM | OperandType.FpuOrAddress };

			ExtendedOpCodeGroup fpuGroupDA = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.fcmovb, new ModRM(0xC0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcmovbe, new ModRM(0xD0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcmove, new ModRM(0xC8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcmovu, new ModRM(0xD8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fucompp, new ModRM(0xE9), true, true, operandTypes: new OperandType[0]),

				new ExtendedOpCode(Mnemonic.fiadd, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.fimul, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.ficom, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.ficomp, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.fisub, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.fisubr, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.fidiv, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.fidivr, ModRM.FromNnn(7))
				);
			fpuGroupDA.OperandTypesByOpCode[0xDA] = new OperandType[] { OperandType.Dword | OperandType.ModRM | OperandType.FpuOrAddress };

			ExtendedOpCodeGroup fpuGroupDB = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.fcmovnb, new ModRM(0xC0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcmovnbe, new ModRM(0xD0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fclex, new ModRM(0xE2), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.finit, new ModRM(0xE3), true, true, operandTypes: new OperandType[0]),
				new ExtendedOpCode(Mnemonic.fcomi, new ModRM(0xF0), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcmovne, new ModRM(0xC8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcmovnu, new ModRM(0xD8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fucomi, new ModRM(0xE8), true, operandTypes: new OperandType[] { OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),

				new ExtendedOpCode(Mnemonic.fild, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.fisttp, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.fist, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.fistp, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.fld, ModRM.FromNnn(5), operandTypes: new OperandType[] { OperandType.DoubleFloatingPoint | OperandType.ModRM | OperandType.FpuOrAddress }),
				new ExtendedOpCode(Mnemonic.fstp, ModRM.FromNnn(7), operandTypes: new OperandType[] { OperandType.DoubleFloatingPoint | OperandType.ModRM | OperandType.FpuOrAddress })
				);
			fpuGroupDB.OperandTypesByOpCode[0xDB] = new OperandType[] { OperandType.Dword | OperandType.ModRM | OperandType.FpuOrAddress };

			ExtendedOpCodeGroup fpuGroupDC = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.fadd, new ModRM(0xC0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcom, new ModRM(0xD0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fsub, new ModRM(0xE0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fdiv, new ModRM(0xF0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fmul, new ModRM(0xC8), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fcomp, new ModRM(0xD8), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fsubr, new ModRM(0xE8), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fdivr, new ModRM(0xF8), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),

				new ExtendedOpCode(Mnemonic.fadd, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.fmul, ModRM.FromNnn(1)),
				new ExtendedOpCode(Mnemonic.fcom, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.fcomp, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.fsub, ModRM.FromNnn(4)),
				new ExtendedOpCode(Mnemonic.fsubr, ModRM.FromNnn(5)),
				new ExtendedOpCode(Mnemonic.fdiv, ModRM.FromNnn(6)),
				new ExtendedOpCode(Mnemonic.fdivr, ModRM.FromNnn(7))
				);
			fpuGroupDC.OperandTypesByOpCode[0xDC] = new OperandType[] { OperandType.DoubleFloatingPoint | OperandType.ModRM | OperandType.FpuOrAddress };

			ExtendedOpCodeGroup fpuGroupDD = new ExtendedOpCodeGroup(
				new ExtendedOpCode(Mnemonic.ffree, new ModRM(0xC0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fist, new ModRM(0xD0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fucom, new ModRM(0xE0), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle, OperandType.FixedRegister | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fstp, new ModRM(0xD8), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),
				new ExtendedOpCode(Mnemonic.fucomp, new ModRM(0xE8), true, operandTypes: new OperandType[] { OperandType.ModRM | OperandType.FpuRegister | OperandType.PackedSingle }),

				new ExtendedOpCode(Mnemonic.fld, ModRM.FromNnn(0)),
				new ExtendedOpCode(Mnemonic.fisttp, ModRM.FromNnn(1), operandTypes: new OperandType[] { OperandType.ModRM | OperandType.Qword | OperandType.FpuOrAddress }),
				new ExtendedOpCode(Mnemonic.fst, ModRM.FromNnn(2)),
				new ExtendedOpCode(Mnemonic.fstp, ModRM.FromNnn(3)),
				new ExtendedOpCode(Mnemonic.frstor, ModRM.FromNnn(4), operandTypes: new OperandType[] { OperandType.ModRM | OperandType.Qword | OperandType.GprOrAddress }),
				new ExtendedOpCode(Mnemonic.fsave, ModRM.FromNnn(6), operandTypes: new OperandType[] { OperandType.ModRM | OperandType.Dword | OperandType.GprOrAddress }),
				new ExtendedOpCode(Mnemonic.fstsw, ModRM.FromNnn(7), operandTypes: new OperandType[] { OperandType.ModRM | OperandType.Word | OperandType.GprOrAddress })
				);
			fpuGroupDD.OperandTypesByOpCode[0xDD] = new OperandType[] { OperandType.DoubleFloatingPoint | OperandType.ModRM | OperandType.FpuOrAddress };

			groups.AddRange(new ExtendedOpCodeGroup[] {
				group1, group1a,
				group2,
				group3a, group3b, //, group3c,
				group4,
				group5a, group5b,
				group6,
				group7a, group7b,
				group8,
				group9,
				// group10, There is no group 10
				group11,
				group12,
				group13,
				group14,
				//group15,
				//group16,
				fpuGroupD8,
				fpuGroupD9,
				fpuGroupDA,
				fpuGroupDB,
				fpuGroupDC,
				fpuGroupDD
			});
		}
		public bool IsExtendedOpCode(int opCode)
		{
			foreach (var group in this._extendedGroups)
			{
				if (group.OperandTypesByOpCode.ContainsKey(opCode))
					return true;
			}

			return false;
		}
		public OpCode GetExtendedOpCode(
			int value,
			ModRM modRM,
			Prefixes prefixes
			)
		{
			foreach (var group in this._extendedGroups)
			{
				if (group.HasOpCodeExtensionPair(value, modRM, prefixes))
					return group.GetOpCode(value, modRM, prefixes);
			}

			OpCode unknownOp = new OpCode(value, 0, OpCodeForm.Normal, Mnemonic.Unknown);
			return unknownOp;
		}
		#endregion
		#endregion

		#region Prefixes
		private Dictionary<int, Prefixes> _prefixTable;

		protected virtual void DefinePrefixes(IDictionary<int, Prefixes> prefixTable)
		{
			prefixTable[Lock] = Prefixes.Lock;
			prefixTable[Repne] = Prefixes.Repne;
			prefixTable[Repe] = Prefixes.Repe;
			prefixTable[CsSegmentOverride] = Prefixes.CsSegmentOverride;
			prefixTable[SsSegmentOverride] = Prefixes.SsSegmentOverride;
			prefixTable[DsSegmentOverride] = Prefixes.DsSegmentOverride;
			prefixTable[EsSegmentOverride] = Prefixes.EsSegmentOverride;
			prefixTable[FsSegmentOverride] = Prefixes.FsSegmentOverride;
			prefixTable[GsSegmentOverride] = Prefixes.GsSegmentOverride;
			prefixTable[BranchNotTaken] = Prefixes.BranchNotTakenHint;
			prefixTable[BranchTaken] = Prefixes.BranchTakenHint;
			prefixTable[OperandSizeOverride] = Prefixes.OperandSizeOverride;
			prefixTable[AddressSizeOverride] = Prefixes.AddressSizeOverride;
		}

		public bool IsPrefix(int n)
		{
			return this._prefixTable.ContainsKey(n);
		}
		public Prefixes GetPrefixCode(int n)
		{
			return this._prefixTable[n];
		}

		#region Group 1
		public static readonly int Lock;
		public static readonly int Repne;
		public static readonly int Repe;
		#endregion
		#region Group 2
		#region Segment overrides
		public static readonly int CsSegmentOverride;
		public static readonly int SsSegmentOverride;
		public static readonly int DsSegmentOverride;
		public static readonly int EsSegmentOverride;
		public static readonly int FsSegmentOverride;
		public static readonly int GsSegmentOverride;
		#endregion
		#region Branch hints
		public static readonly int BranchNotTaken;
		public static readonly int BranchTaken;
		#endregion
		#endregion
		#region Group 3
		public static readonly int OperandSizeOverride;
		#endregion
		#region Group 4
		public static readonly int AddressSizeOverride;
		#endregion
		#endregion

		public static readonly byte EscapeCode = 0x0F;
	}
}
